﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows;
using System.Windows.Markup;
using Xceed.Silverlight.ListBox.Utils;
using System.Windows.Threading;
using System.Xml;
using System.IO;

namespace Xceed.Silverlight.ListBox
{
  public partial class ListBox
  {
    #region Internal Methods

    internal ListBoxContainerConfigurationBase GetCachedContainerConfiguration( ConfigurationType configurationType )
    {
      if( configurationType == ConfigurationType.All )
        throw new ArgumentException( "The specified ConfigurationType is not valid.", "containerType" );

      // Check if there's a cached entry in the dictionary.
      ListBoxContainerConfigurationBase containerConfiguration = null;
      if( m_containerTypeConfigurationDictionary.TryGetValue( configurationType, out containerConfiguration ) )
      {
        return containerConfiguration;
      }

      // Cached entry not found.  Determine the one to use.
      containerConfiguration = this.BuildContainerConfiguration( configurationType );

      // If the container configuration is null, then we are dealing with 
      // an unknown container type for which the GetContainer method did not return
      // a proper configuration.
      if( containerConfiguration == null )
      {
        throw new InvalidOperationException( "No container configuration could be retrieved for container type " + configurationType + "." );
      }

      // Now cache it.
      m_containerTypeConfigurationDictionary.Add( configurationType, containerConfiguration );

      return containerConfiguration;
    }

    #endregion

    #region Protected Methods

    protected internal void InvalidateContainerConfigurationCache( ConfigurationType configurationType )
    {
      if( configurationType == ConfigurationType.All )
        return;

      // Remove from the dictionary the entry for the know configuration type
      // so that it is recalculated in the next Layout pass through a call to PrepareContainer.
      if( m_containerTypeConfigurationDictionary.ContainsKey( configurationType ) )
      {
        ListBoxContainerConfigurationBase configuration = m_containerTypeConfigurationDictionary[ configurationType ];

        CachedSizeChangedManager.RemoveListener( configuration, this );
        m_containerTypeConfigurationDictionary.Remove( configurationType );

        Debug.WriteLine( "CONFIGURATION CACHE INVALIDATED FOR CONFIGURATION TYPE: " + configurationType + "." );

        // Only dispatch a call to Refresh if the configuration for the container type
        // was cached in the dictionary.  
        // Else, there is no change to the configuration to use logic and 
        // therefore no need to refresh.
        if( m_isLoaded )
        {
          this.DeferRefresh();
        }
      }
    }

    internal void ClearContainerConfigurationCache()
    {
      if( m_containerTypeConfigurationDictionary.Count > 0 )
      {
        foreach( ListBoxContainerConfigurationBase configuration in m_containerTypeConfigurationDictionary.Values )
        {
          CachedSizeChangedManager.RemoveListener( configuration, this );
        }

        m_containerTypeConfigurationDictionary.Clear();
        Debug.WriteLine( "ALL CONFIGURATIONS CACHE CLEARED." );

        // Only dispatch a call to Refresh if the configuration for the container type
        // was cached in the dictionary.  
        // Else, there is no change to the configuration to use logic and 
        // therefore no need to refresh.
        if( m_isLoaded )
        {
          this.DeferRefresh();
        }
      }

      m_clearContainerConfigurationCacheOperation = null;
    }

    internal void DeferClearContainerConfigurationCache()
    {
      if( m_clearContainerConfigurationCacheOperation != null )
        return;

      m_clearContainerConfigurationCacheOperation = this.Dispatcher.BeginInvoke( new Action( this.ClearContainerConfigurationCache ) );
    }

    private DispatcherOperation m_clearContainerConfigurationCacheOperation = null;

    #endregion

    #region Private Methods

    private ListBoxContainerConfigurationBase BuildContainerConfiguration( ConfigurationType configurationType )
    {
      ContainerConfigurations listBoxContainerConfigurations = this.ContainerConfigurations;

      // If for some reason the instance is null, revert to a new instance.
      if( listBoxContainerConfigurations == null )
        listBoxContainerConfigurations = new ContainerConfigurations();

      // View has ContainerConfigurationsBase as base class
      ContainerConfigurationsBase viewConfigurations = null;

      ListBoxContainerConfigurationBase containerConfiguration = null;

      if( configurationType == ConfigurationType.Item )
      {
        ListBoxContainerConfigurationBase itemConfiguration = ( viewConfigurations != null )
          ? viewConfigurations.ListBoxItemConfiguration
          : null;

        // We found a view, complete it with the values on the listBoxContainerConfigurations
        if( ( itemConfiguration != null ) && ( listBoxContainerConfigurations.ListBoxItemConfiguration != null ) )
        {
          // clone the value of the configuration found on the view before completing it
          itemConfiguration = itemConfiguration.CreateInstance();

          this.CompleteContainerConfigurationFromContainerConfigurations(
            listBoxContainerConfigurations.ListBoxItemConfiguration,
            itemConfiguration );

          containerConfiguration = itemConfiguration;
        }
        else
        {
          // Get the ItemConfiguration from the ContainerConfigurations
          if( itemConfiguration == null )
            itemConfiguration = listBoxContainerConfigurations.ListBoxItemConfiguration;

          // Get the Default on the ListBox ContainerConfigurations
          if( itemConfiguration == null )
            itemConfiguration = listBoxContainerConfigurations.GetContainerConfiguration( configurationType );

          containerConfiguration = itemConfiguration.CreateInstance();
        }

        Debug.Assert( containerConfiguration != null );

        this.CompleteContainerConfigurationFromListBox( containerConfiguration );
      }
      else if( configurationType == ConfigurationType.GroupHeader )
      {
        ListBoxContainerConfigurationBase groupContainerConfiguration = ( viewConfigurations != null )
         ? viewConfigurations.ListBoxGroupHeaderConfiguration
         : null;

        // Get the ItemConfiguration from the ContainerConfigurations
        if( groupContainerConfiguration == null )
          groupContainerConfiguration = listBoxContainerConfigurations.ListBoxGroupHeaderConfiguration;

        // Get the Default on the ListBox ContainerConfigurations
        if( groupContainerConfiguration == null )
          groupContainerConfiguration = listBoxContainerConfigurations.GetContainerConfiguration( configurationType );

        Debug.Assert( groupContainerConfiguration != null );

        containerConfiguration = groupContainerConfiguration.CreateInstance();
      }
      else
      {
        throw new InvalidOperationException(
          "An error occurred trying to create a ListBoxContainerConfigurationBase instance for the specified configuration type " + configurationType + "." );
      }

      if( containerConfiguration == null )
      {
        throw new InvalidOperationException(
          "An error occurred trying to create a ListBoxContainerConfigurationBase instance for the specified configuration type " + configurationType + "." );
      }

      // We only want to listen to the built configurations size changed event since
      // those are the one we will modify the cached size on.
      CachedSizeChangedManager.AddListener( containerConfiguration, this );

      return containerConfiguration;
    }

    private void CompleteContainerConfigurationFromContainerConfigurations(
      ListBoxContainerConfigurationBase source,
      ListBoxContainerConfigurationBase target )
    {
      if( ( target == null ) || ( source == null ) )
        return;

      if( target.ContainerStyle == null )
      {
        target.ContainerStyle = source.ContainerStyle;
      }

      if( target.ContentTemplate == null )
      {
        target.ContentTemplate = source.ContentTemplate;
      }
    }

    private void CompleteContainerConfigurationFromListBox(
     ListBoxContainerConfigurationBase target )
    {
      if( target == null )
        return;

      if( target.ContainerStyle == null )
      {
        target.ContainerStyle = this.ItemContainerStyle;
      }

      if( target.ContentTemplate == null )
      {
        DataTemplate itemTemplate = this.ItemTemplate;
        string displayMemberPath = this.DisplayMemberPath;

        if( ( itemTemplate != null ) && ( !String.IsNullOrEmpty( displayMemberPath ) ) )
        {
          throw new InvalidOperationException( "Cannot set DisplayMemberPath and ItemTemplate simultaneously ." );
        }

        // Priorize the ListBox API ItemTemplate over Display
        if( itemTemplate != null )
        {
          target.ContentTemplate = itemTemplate;
        }
        else if( !String.IsNullOrEmpty( displayMemberPath ) )
        {
#if SILVERLIGHT
          target.ContentTemplate =
            XamlReader.Load( ListBox.DisplayMemberTemplateXAML.Replace( "%PATH%", displayMemberPath ) ) as DataTemplate;
#else
          XmlReader xmlReader = XmlReader.Create( new StringReader( ListBox.DisplayMemberTemplateXAML.Replace( "%PATH%", displayMemberPath ) ) );
          target.ContentTemplate = XamlReader.Load( xmlReader ) as DataTemplate;
#endif
        }
      }
    }

    #endregion

    #region Private Fields

    private Dictionary<ConfigurationType, ListBoxContainerConfigurationBase> m_containerTypeConfigurationDictionary =
      new Dictionary<ConfigurationType, ListBoxContainerConfigurationBase>( 4 );

    #endregion

  }
}
